iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0

23 - Speech Synthesis

tags: JavaScript30

專案簡介

第二十三天要做的專案是學習語音撥放的 API,做出可以閱讀文字輸入的語音撥放器,並且可以調整語速及語調

課程影片:JS30 23
導讀影片:Alex

初始文件

Github 檔案位置:23 - Speech Synthesis

網站初始的樣子

可以先玩玩 最後的成品

正式製作

首先一開始還是選取所有要操控的元素

今天要用到的 API 就是 SpeechSynthesisUtterance,他可以利用電腦內建的語音服務去閱讀文字內容

const msg = new SpeechSynthesisUtterance();
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
const options = document.querySelectorAll('[type="range"], [name="text"]');
const speakButton = document.querySelector('#speak');
const stopButton = document.querySelector('#stop');
msg.text = document.querySelector('[name="text"]').value;

這個 API 要求我們在撥放前選取好要使用的語音服務 voice,因此這裡利用 speechSynthesis.getVoices() 獲取所有可用的語音服務,

再透過 filter 把服務限縮到英文和中文語音,並利用 map 把陣列的資料整理為可視化的 HTML 元素,最後以 join 將其合併並消除逗號

function populateVoices() {
  voices = speechSynthesis.getVoices()
  // console.log(voices);
  voicesDropdown.innerHTML = voices
    .filter(voice => voice.lang.includes('en') || voice.lang.includes('zh-TW'))
    .map(voice => `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
    .join('');
}

speechSynthesis.addEventListener('voiceschanged', populateVoices);

就得到只有中英文的語音服務選單了

備註:voiceschanged 事件在加載進網頁時會自動觸發一次,在此專案中僅利用他建立出語系清單,後續會再監聽語系的切換


接下來就是要實現語音撥放以及語速、語調等係數的調整了

以下來讓我們介紹一下各參數的設定

speechSynthesis 名詞介紹

  • speechSynthesis.cancel() 清空上次的撥放內容
  • speechSynthesis.speak() 執行語音朗讀
  • msg.voice 切換撥放的語音服務
  • msg.rate 調整撥放的語速
  • msg.pitch 調整撥放的語調
  • msg.text 指定要語音朗讀的文字

在這裡我們先實作撥放的功能,再切換語速及語調

這裡的 voices 是我們剛才提取出的所有語系,在語系清單的 change 事件觸發時,就會將目前選取的語系賦值給 msg.voice

接著跟 Day1 將撥放時間歸零的方式很像,在撥放前先將正在撥放的音訊清除,否則要等到前一段撥放完下一段才會再次撥放

startOver = true 是 ES6 的新語法,可以在沒有變數傳入時默認 startOver 值為 true,這樣只要在需要停止音樂時傳入 false 即可做到開關

function setVoice() {
  msg.voice = voices.find(voice => voice.name === this.value);
  toggle();
}

function toggle(startOver = true) {
  speechSynthesis.cancel();
  if (startOver) {
    speechSynthesis.speak(msg);
  }
}


voicesDropdown.addEventListener('change', setVoice);
speakButton.addEventListener('click', toggle);
stopButton.addEventListener('click', () => toggle(false));

最後這裡是調整語速、語調、朗讀內容的部分,只要使用者改變了這些值就會觸發 setOption(),這裡的 this.name 是 HTML 中原先就設定好的值

const options = document.querySelectorAll('[type="range"], [name="text"]');

function setOption() {
  console.log(this.name, this.value);
  msg[this.name] = this.value;
  toggle();
}

options.forEach(option => option.addEventListener('change', setOption));

最後程式碼

const msg = new SpeechSynthesisUtterance();
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
const options = document.querySelectorAll('[type="range"], [name="text"]');
const speakButton = document.querySelector('#speak');
const stopButton = document.querySelector('#stop');
msg.text = document.querySelector('[name="text"]').value;

function populateVoices() {
  console.log('owo');
  voices = this.getVoices();
  // console.log(voices);
  voicesDropdown.innerHTML = voices
    .filter(voice => voice.lang.includes('en') || voice.lang.includes('zh-TW'))
    .map(voice => `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
    .join('');
}

function setVoice() {
  msg.voice = voices.find(voice => voice.name === this.value);
  toggle();
}

function toggle(startOver = true) {
  speechSynthesis.cancel();
  if (startOver) {
    speechSynthesis.speak(msg);
  }
}

function setOption() {
  console.log(this.name, this.value);
  msg[this.name] = this.value;
  toggle();
}

speechSynthesis.addEventListener('voiceschanged', populateVoices);
voicesDropdown.addEventListener('change', setVoice);
options.forEach(option => option.addEventListener('change', setOption));
speakButton.addEventListener('click', toggle);
stopButton.addEventListener('click', () => toggle(false));

完成結果圖

最後的成品

結語

以上是第二十三天的製作紀錄,如有錯誤或不足的地方還請多多指教 >.<

JavaScript Text-To-Speech - #JavaScript30 23/30
[ Alex 宅幹嘛 ] 深入淺出 Javascript30 快速導覽 | Day 23 - Speech Synthesis
MDN Web Docs


上一篇
JS30 -> 22:Follow Along Link Highliter
下一篇
JS30 -> 24 - Sticky Nav
系列文
剛接觸前端一個月的小白 - JavaScript30 挑戰筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言